package com.atguigu.filter;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.result.RetVal;
import com.atguigu.result.RetValCodeEnum;
import com.atguigu.util.IpUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.util.List;

//这是一个全局拦截器
@Component
public class AccessFilter implements GlobalFilter {

    @Value("${filter.whiteList}")
    private String filterWhiteList;
    @Autowired
    private RedisTemplate redisTemplate;

    /**
     *
     * @param exchange 服务网络交换器 存放请求-响应信息 请求实例响应实例
     * @param chain   网关过滤链表 放开拦截 设计模式:责任链设计模式
     *               设计模式 spring源码 数据结构 算法 离散数学 概率论 网络编程 操作系统 编译原理
     * @return
     */

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        String path = request.getURI().getPath();
        //获取用户临时id
        String userTempId=getUserTempId(request);
        //获取用户的id
        String userId=getUserId(request);
        //4.把用户的id保存到header中 传给shop-web那边
        if(!StringUtils.isEmpty(userTempId)){
            request.mutate().header("userTempId",userTempId).build();
        }
        if(!StringUtils.isEmpty(userId)){
            request.mutate().header("userId",userId).build();
        }

        //1.请求白名单 如果是白名单里面请求 必须登录
        String[] whiteList = filterWhiteList.split(",");
        for (String white : whiteList) {
            //用户访问的路径包含白名单里面的请求路径 用户未登录
            if(path.indexOf(white)!=-1&&"-1".equals(userId)){
                //重定向跳转到登录界面
                ServerHttpResponse response = exchange.getResponse();
                response.setStatusCode(HttpStatus.SEE_OTHER);
                response.getHeaders().set(HttpHeaders.LOCATION,"http://passport.gmall.com/login.html?originalUrl="+request.getURI());
                return response.setComplete();
            }
        }
        //2.内部接口不允许访问 如请求路径当中包含sku
        //匹配路径对象
        AntPathMatcher antPathMatcher=new AntPathMatcher();
        if(antPathMatcher.match("/sku/**",path)){
            return writeDataToBrowser(exchange);
        }
        //3.对于某些请求资源(我的购物车，我的订单，我的账号,xxxx) 必须先登录
        //过滤器放开拦截 让下游请求继续执行
        return chain.filter(exchange);
    }

    private String getUserTempId(ServerHttpRequest request) {
        List<String> headerValueList = request.getHeaders().get("userTempId");
        String userTempId="";
        if(!CollectionUtils.isEmpty(headerValueList)){
            userTempId=headerValueList.get(0);
        }else{
            HttpCookie cookie = request.getCookies().getFirst("userTempId");
            if(cookie!=null){
                userTempId=cookie.getValue();
            }
        }
        return userTempId;
    }

    //写数据给浏览器
    private Mono<Void> writeDataToBrowser(ServerWebExchange exchange) {
        //不让其跳转到该接口
        ServerHttpResponse response = exchange.getResponse();
        //要写什么数据
        RetVal<Object> retVal = RetVal.build(null, RetValCodeEnum.NO_PERMISSION);
        byte[] bytes = JSONObject.toJSONString(retVal).getBytes(StandardCharsets.UTF_8);
        DataBuffer dataBuffer=response.bufferFactory().wrap(bytes);
        //设置返回给浏览器的数据类型
        response.getHeaders().add("Content-Type","application/json;charset=UTF-8");
        //把数据写给浏览器
        return response.writeWith(Mono.just(dataBuffer));
    }

    private String getUserId(ServerHttpRequest request) {
        //a.获取登录的token(从header和cookie中拿)
        List<String> headerValueList = request.getHeaders().get("token");
        String token="";
        if(!CollectionUtils.isEmpty(headerValueList)){
            token=headerValueList.get(0);
        }else{
            HttpCookie cookie = request.getCookies().getFirst("token");
            if(cookie!=null){
                token=cookie.getValue();
            }
        }
        //b.根据token查询redis
        if(!StringUtils.isEmpty(token)){
            String userKey= "user:login:"+token;
            //注意redis编码一致问题 需要加上配置类redisconfig
            String loginInfoJson=(String) redisTemplate.opsForValue().get(userKey);
            JSONObject loginJsonObject = JSONObject.parseObject(loginInfoJson);
            String loginIp = loginJsonObject.getString("loginIp");
            //拿取当前请求的ip地址
            String requestIpAddress = IpUtil.getGatwayIpAddress(request);
            if(loginIp.equals(requestIpAddress)){
                return loginJsonObject.getString("userId");
            }else{
                return "-1";
            }
        }
        return null;
    }
}
